//
//  VirtualBaseClass.hpp
//  learnC++
//
//  Created by liuzhibing on 2018/6/17.
//  Copyright © 2018年 yunfan.com. All rights reserved.
//

#ifndef VirtualBaseClass_hpp
#define VirtualBaseClass_hpp

#include <stdio.h>
#include <iostream>
//菱形继承问题在此解决(使用虚拟基类)
//要共享基类，只需在派生类的继承列表中插入“虚拟”关键字即可。 这创建了所谓的虚拟基类，这意味着只有一个共享的基础对象。

class PoweredDevice
{
public:
    PoweredDevice(int power)
    {
        std::cout << "PoweredDevice: " << power << '\n';
    }
};

class Scanner: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class
{
public:
    Scanner(int scanner, int power)
    : PoweredDevice(power) // this line is required to create Scanner objects, but ignored in this case
    {
        std::cout << "Scanner: " << scanner << '\n';
    }
};

class Printer: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class
{
public:
    Printer(int printer, int power)
    : PoweredDevice(power) // this line is required to create Printer objects, but ignored in this case
    {
        std::cout << "Printer: " << printer << '\n';
    }
};

class Copier: public Scanner, public Printer
{
public:
    Copier(int scanner, int printer, int power)
    : Scanner(scanner, power), Printer(printer, power),
    PoweredDevice(power) // PoweredDevice is constructed here
    {
    }
    
//    因为Copier负责创建PoweredDevice，而不是Scanner或Printer
};

/*
 
 Copier copier(1, 2, 3);
 
 首先，虚拟基类总是在非虚拟基类之前创建的，这确保所有基类都在其派生类之前创建。
 
 其次，请注意Scanner和Printer构造函数仍然有对PoweredDevice构造函数的调用。创建复印机实例时，这些构造函数调用将被忽略，因为复印机负责创建PoweredDevice，而不是扫描仪或打印机。但是，如果我们要创建Scanner或Printer的实例，那么将使用这些构造函数调用，并应用正常的继承规则。
 
 第三，如果一个类继承了一个或多个拥有虚拟父母的类，那么派生类最多的是负责构建虚拟基类。在这种情况下，复印机会继承打印机和扫描仪，它们都具有一个PoweredDevice虚拟基类。 Copier是派生最多的类，负责创建PoweredDevice。请注意，即使在单个继承情况下也是如此：如果Copier是从Printer单独继承的，并且Printer实际上是从PoweredDevice继承的，则Copier仍负责创建PoweredDevice。
 
 第四，虚拟基类一直被认为是其派生类最直接的基础（这就是为什么大多数派生类负责其构造）。但是继承虚拟基础的类仍然需要访问它。因此，为了便于实现，编译器会为每个直接继承虚拟类（Printer和Scanner）的类创建一个虚拟表。这些虚拟表格指向最派生类中的函数。因为派生类有一个虚拟表，这也意味着它们现在被一个指针（指向虚拟表）所增大。

 */


#endif /* VirtualBaseClass_hpp */
